#if !PocketPC || Framework20
using System;
using System.Text;
using System.Threading;
using System.Collections;
using System.Reflection;
using GeoFramework;

namespace GeoFramework.Threading
{
#if !PocketPC || DesignTime
#if Framework20
    [Obfuscation(Feature = "renaming", Exclude = false, ApplyToMembers = true)]
    [Obfuscation(Feature = "controlflow", Exclude = true, ApplyToMembers = true)]
    [Obfuscation(Feature = "stringencryption", Exclude = false, ApplyToMembers = true)]
#endif
#endif
	internal sealed class ThreadPoolShutdownSignal
    {
        public ThreadPoolShutdownSignal() {}

        ~ThreadPoolShutdownSignal()
        {
            ThreadPool.Dispose();
        }
    }

#if !PocketPC || DesignTime
#if Framework20
    [Obfuscation(Feature = "renaming", Exclude = false, ApplyToMembers = true)]
    [Obfuscation(Feature = "controlflow", Exclude = true, ApplyToMembers = true)]
    [Obfuscation(Feature = "stringencryption", Exclude = false, ApplyToMembers = true)]
#endif
#endif
    internal sealed class ThreadPoolItem
    {
        private WaitCallback pMethod;
        private object pState;

        public ThreadPoolItem(WaitCallback method, object state)
        {
            pMethod = method;
            pState = state;
        }

        public WaitCallback Method
        {
            get
            {
                return pMethod;
            }
        }

        public object State
        {
            get
            {
                return pState;
            }
        }
    }

    /// <summary>
    /// Represents a group of threads used to execute lightweight methods.
    /// </summary>
    /// <remarks><para>While the .NET Framework has its own ThreadPool class in the
    /// System.Threading namespace, the class is missing some important features
    /// which can affect performance.  This class was created to give developers
    /// greater control over the thread pool for better results.  The queue of
    /// methods waiting to be executed can be cleared, helping applications to
    /// shut down more gracefully.  The prioirty of all threads in the pool can
    /// be adjusted on-the-fly to give applications the best performance.  The
    /// maximum number of threads lets developers control how much of system 
    /// resources to use, and the maximum thread idle time keeps the active thread
    /// count as low as possible to maximum performance.</para>
    /// <para>The performance of this class appears to exceed that of .NET's own
    /// ThreadPool due to the ability to control thread priority.  Additionally, the .NET
    /// ThreadPool seems to launch new threads for each request, whereas this class will
    /// reuse existing threads whenever possible, resulting in less time spend creating
    /// and tearing down threads.</para>
    /// <para>This class is completely thread-safe.  Methods are executed in
    /// the same order in which they are queued.  Rarely, methods may execute out-of-order
    /// depending on how complex the methods are.  If an absolute guarantee of execution
    /// order is needed, thread pools should not be used.</para>
    /// </remarks>
#if !PocketPC || DesignTime
#if Framework20
    [Obfuscation(Feature = "renaming", Exclude = false, ApplyToMembers = true)]
    [Obfuscation(Feature = "controlflow", Exclude = true, ApplyToMembers = true)]
    [Obfuscation(Feature = "stringencryption", Exclude = false, ApplyToMembers = true)]
#endif
#endif
	public 
#if Framework20
        static
#else
        sealed 
#endif
    class ThreadPool //: IDisposable
	{
        private static Queue pMethodQueue = new Queue();
        private static ArrayList pThreads = new ArrayList(8);
        private static ManualResetEvent QueueWaitHandle = new ManualResetEvent(false);
#if !PocketPC
	#if Framework20
			private static ThreadPriority pThreadPriority = ThreadPriority.Normal;
			private static int pMaximumThreads = 25;
	#else
			private static ThreadPriority pThreadPriority = ThreadPriority.Normal;
			private static int pMaximumThreads = 10;
	#endif
#else
	#if Framework20
        private static ThreadPriority pThreadPriority = ThreadPriority.BelowNormal;
        private static int pMaximumThreads = 5;
	#elif Smartphone
		private static ThreadPriority pThreadPriority = ThreadPriority.Normal;
		private static int pMaximumThreads = 5;
	#else
        private static ThreadPriority pThreadPriority = ThreadPriority.Normal;
        private static int pMaximumThreads = 5;
	#endif
#endif
        private static int pBusyThreads;
        private static bool pIsAlive = true;
        private static TimeSpan pMaximumIdleTime = TimeSpan.FromSeconds(10);
        private static int pMaximumQueueSize = 100;
        private static ThreadPoolShutdownSignal ShutdownSignal = new ThreadPoolShutdownSignal(); 

        //// Private class
        //static ThreadPool()
        //{
        //    ShutdownSignal = new ThreadPoolShutdownSignal();
        //}

        /// <summary>
        /// Cancels any pending methods and shuts down all active threads.
        /// </summary>
        /// <remarks>This method is called automatically when an application exits.
        /// Calling this method manually causes all threads to be shut down.  Any
        /// subsequent calls to the Queue method will fail.  This method must only
        /// be called when an application shutdown is in progress and will not be 
        /// cancelled.</remarks>
        public static void Dispose()
        {
            // Are we already disposed? If so, just exit
            if (!pIsAlive)
                return;

			// Shut down any active threads
			pIsAlive = false;

            try
            {
                // Clear the method queue
                lock (pMethodQueue.SyncRoot)
                {
                    // Remove all items
                    pMethodQueue.Clear();
                }

                // Signal all threads to wake up so that they can exit          
#if Framework20 && !PocketPC
                if (!QueueWaitHandle.SafeWaitHandle.IsClosed)
#else
                if (QueueWaitHandle.Handle != new IntPtr(-1))
#endif
                {
                    try
                    {
                        QueueWaitHandle.Set();
                    }
                    catch
                    {
                    }
                }

                // Wait a sec for pool threads to exit
                Thread.Sleep(0);

                // Abort all pending threads
                lock (pThreads.SyncRoot)
                {
                    // Tear down each thread
                    foreach (Thread thread in pThreads)
                    {
                        try
                        {
                            thread.Abort();
                        }
                        catch { }
                    }

                    // Remove all items
                    pThreads.Clear();
                }
                Thread.Sleep(0);

                // Close the wait handle
#if Framework20 && !PocketPC
                if (!QueueWaitHandle.SafeWaitHandle.IsClosed)
#else
            if (QueueWaitHandle.Handle != new IntPtr(-1))
#endif
                {
                    try
                    {
                        QueueWaitHandle.Close();
                    }
                    catch
                    {
                    }
                }
            }
            catch
            {
                // This is called during a finalizer, so errors must be suppressed :P
            }
        }

        //void IDisposable.Dispose()
        //{
        //    Dispose();
        //}

        /// <summary>
        /// Removes any pending methods from the queue.
        /// </summary>
        /// <remarks>This method will clear the queue of all methods waiting to
        /// execute.  This method is typically called via the Dispose method to 
        /// gracefully shut down the thread pool.  This method should only be
        /// called if the methods waiting to execute are not critical.</remarks>
        public static void Clear()
        {
            lock (pMethodQueue.SyncRoot)
            {
                pMethodQueue.Clear();
            }
        }

        /// <summary>
        /// Adds a method to the thread pool for execution.
        /// </summary>
        /// <param name="method">A WaitCallback delegate pointing to a method to execute.</param>
        /// <remarks>This method is used to execute a method on the thread pool.
        /// Depending on the maximum number of threads allowed, the method will be
        /// executed either immediately, or as soon as a thread becomes available.
        /// If resources allow, a new thread may be started to service the queue.</remarks>
        public static void Queue(WaitCallback method)
        {
            Queue(method, null);
        }

        /// <summary>
        /// Adds a method to the thread pool for execution.
        /// </summary>
        /// <param name="method">A WaitCallback delegate pointing to a method to execute.</param>
        /// <remarks>This method is used to execute a method on the thread pool.
        /// Depending on the maximum number of threads allowed, the method will be
        /// executed either immediately, or as soon as a thread becomes available.
        /// If resources allow, a new thread may be started to service the queue.</remarks>
        public static void Queue(WaitCallback method, object state)
        {
            // Are we shut down?
            if (!pIsAlive)
                throw new ObjectDisposedException("ThreadPool", "The thread pool has been disposed because the application is terminating.  As a result, requests to queue new methods are no longer allowed.");

//            // Wait for the queue to shrink under the maximum
//            while (pMethodQueue.Count >= pMaximumQueueSize)
//            {
//                Thread.Sleep(250);
//                Thread.Sleep(0);
//            }

            // Add the method to the queue
            lock (pMethodQueue.SyncRoot)
            {
                pMethodQueue.Enqueue(new ThreadPoolItem(method, state));
            }

            // And flag that we have something to execute
            try
            {
                QueueWaitHandle.Set();
            }
            catch (ObjectDisposedException)
            {
                // Just exit
                return;
                //throw new ObjectDisposedException("ThreadPool", "The thread pool has been disposed because the application is terminating.  As a result, requests to queue new methods are no longer allowed.");
            }

            // Do we need to launch a new thread to service this request?            
            if (pThreads.Count == 0 || (pBusyThreads >= pThreads.Count && pBusyThreads < pMaximumThreads))
            {
                // Yes. Start a new thread
                Thread NewPoolThread = new Thread(new ThreadStart(PoolThreadProc));

                // Add the thread to the pool
                lock (pThreads.SyncRoot)
                {
                    pThreads.Add(NewPoolThread);
                }

                // And rev it up
#if !PocketPC || Framework20
                NewPoolThread.Name = "GeoFrameworks Thread Pool Thread (http://www.geoframeworks.com)";
                NewPoolThread.IsBackground = true;
#endif

                // Don't set priority on DTMC machines
#if !Framework10
                if(Environment.OSVersion.Platform == PlatformID.WinCE)
#endif
                    NewPoolThread.Priority = pThreadPriority;

                NewPoolThread.Start();

				// Let it start up
				Thread.Sleep(0);

            }
        }

        /// <summary>
        /// Controls the maximum allowed threads in the pool.
        /// </summary>
        /// <remarks><para>This class will create new threads only if all other active threads
        /// are busy and the current thread count is less than the maximum number of
        /// threads allowed.  Increasing this property will not cause new threads to
        /// be created immediately -- it will only allow for new threads to be created when they
        /// are actually needed.  If this property is decreased, then any extra threads
        /// in the thread pool will terminate until the current thread count it at
        /// the maximum.</para>
        /// <para>To maximumize performance, threads in the pool which have been idle for the
        /// amount of time specified in MaximumIdleTime will exit.  This allows
        /// the thread pool to automaitcally expand and shrink based on the changing demands 
        /// of the application.</para>
        /// <para>For Compact Framework applications (and especially Smartphone applications), 
        /// resources for multithreading are very limited, so the maximum thread count should 
        /// rarely exceed ten.</para></remarks>
        public static int MaximumThreads
        {
            get 
            {
                return pMaximumThreads;
            }
            set
            {
                // Is the new value zero or less?
                if (value <= 0)
#if !PocketPC || Framework20
                    throw new ArgumentOutOfRangeException("MaximumThreads", Resources.ThreadPool_InvalidMaximumThreads);
#else
					throw new ArgumentOutOfRangeException(Resources.ThreadPool_InvalidMaximumThreads);
#endif
                // Set the new value
                pMaximumThreads = value;               
            }
        }


        /// <summary>
        /// Controls the maximum number of methods allowed in the queue.
        /// </summary>
        /// <remarks><para>The method queue is a Queue object which contains a list of all delegates waiting
        /// to be executed by the thread pool.  If there are not enough threads in the pool, the
        /// method queue can start to grow out of control.  Ideally, there will be enough threads in
        /// the pool to execute methods as fast as they become queued.  This property causes an exception
        /// to be raised if the queue grows too large.</para>
        /// <para>If your application experiences a significant delay between the
        /// time a method is queued and the time it actually executes, consider increasing the
        /// MaximumThreads property.  This will allow more threads to work simultaneously to
        /// execute queued methods.</para>
        /// <para>If the queue becomes too full, the Queue method will block until the queue
        /// decreases in size.  Sudden application hangs can be caused by this blocking, so
        /// examine the CurrentQueueSize property to see if the queue is overflowing and increase
        /// MaximumThreads to compensate.</para></remarks>
        public static int MaximumQueueSize
        {
            get
            {
                return pMaximumQueueSize;
            }
            set
            {
                if (value <= 0)
#if !PocketPC || Framework20
                    throw new ArgumentOutOfRangeException("MaximumQueueSize", Resources.ThreadPool_InvalidMaximumQueueSize);
#else
					throw new ArgumentOutOfRangeException(Resources.ThreadPool.InvalidMaximumQueueSize);
#endif
                pMaximumQueueSize = value;
            }
        }

        /// <summary>
        /// Returns the current number of active threads in the thread pool.
        /// </summary>
        /// <remarks><para>The thread pool is designed to create new threads if no
        /// other thread is already available to service the method queue.  This
        /// property is typically examined to see how many threads the thread pool
        /// actually needs to keep up with the application.</para>
        /// <para>If this property is almost always equal to MaximumThreads, there
        /// may not be enough threads allowed to keep up with the method queue.  
        /// Consider increasing the MaximumThreads property to compensate.</para></remarks>
        public static int CurrentThreads
        {
            get
            {
                return pThreads.Count;
            }
        }

        /// <summary>
        /// Returns the number of threads which are currently executing methods.
        /// </summary>
        /// <remarks><para>This property is used to see how many threads are actually
        /// executing methods.  The value of this property should typically be less than
        /// the MaximumThreads property.  If this property is almost always equal to MaximumThreads, there
        /// may not be enough threads allowed to keep up with the method queue.  
        /// Consider increasing the MaximumThreads property to compensate.</para></remarks>
        public static int BusyThreads
        {
            get
            {
                return pBusyThreads;
            }
        }

        /// <summary>
        /// Returns the number of methods waiting to be executed by the thread pool.
        /// </summary>
        /// <remarks>This property is typically used to see if there are enough threads
        /// allowed to keep the size of the method queue low.  If there are not enough threads,
        /// the method queue will grow faster than threads can execute them.  This can result
        /// in significant application latency.  If this property is near the 
        /// MaximumQueueSize property, consider increasing the MaximumThreads property
        /// to compensate.</remarks>
        public static int CurrentQueueSize
        {
            get
            {
                return pMethodQueue.Count;
            }
        }

        /// <summary>
        /// Controls the maximum time a thread is allowed to be idle before shutting down.
        /// </summary>
        /// <remarks>If an application uses the thread pool a lot, but the activity starts to
        /// taper off, threads in the thread pool may no longer be required.  If a thread in 
        /// the pool does not execute any method for a period of time, it will exit in order
        /// to release system resources.  This property controls the period of time a thread is
        /// allowed to remain idle before it terminates.  This property is typically a value
        /// of thirty or more seconds.  A value of over five or ten seconds is recommended
        /// in order to allow threads to be re-used.  Higher values should be used when system
        /// resources are not so much of a concern, such as for the Desktop Framework applications.</remarks>
        public static TimeSpan MaximumIdleTime
        {
            get
            {
                return pMaximumIdleTime;
            }
            set
            {
                if (value.TotalSeconds <= 0)
#if !PocketPC || Framework20
                    throw new ArgumentOutOfRangeException("MaximumIdleTime", "The maximum thread pool idle time must be a value greater than zero.  A value of 30 or more seconds is recommended.");
#else
					throw new ArgumentOutOfRangeException("The maximum thread pool idle time must be a value greater than zero.  A value of 30 or more seconds is recommended.");
#endif
				pMaximumIdleTime = value;
            }
        }

#if PocketPC || !Framework20
        private static ThreadPriority ThreadPriority
        {
            get
            {
                return pThreadPriority;
            }
            set
            {
                if (pThreadPriority == value)
                    return;
                // Set the new prioirty for all threads
                foreach (Thread item in pThreads)
                {
                    item.Priority = pThreadPriority;
                }
            }
        }
#endif

        private static void PoolThreadProc()
        {
            try
            {
                DateTime LastExecutedMethod = DateTime.Now;

                while (pIsAlive)
                {
                    // Wait for something to get queued
                    try
                    {
                        QueueWaitHandle.WaitOne();
                    }
                    catch (ObjectDisposedException)
                    {
                        break;
                    }

                    // Are we exiting?  If so, leave immediately
                    if (!pIsAlive)
                        return;

                    // Is the queue ready to work with?
                    ThreadPoolItem method = null;
                    lock (pMethodQueue.SyncRoot)
                    {
                        // Is there something in the queue?
                        if (pMethodQueue.Count > 0)
                        {
                            // Yes.  De-queue it
                            method = pMethodQueue.Dequeue() as ThreadPoolItem;

                            // And signal that we're not idle
                            LastExecutedMethod = DateTime.Now;
                        }
                        // If the queue is now empty, reset the wait handle
                        try
                        {
                            if (pMethodQueue.Count > 0)
                                QueueWaitHandle.Set();
                            else
                                QueueWaitHandle.Reset();
                        }
                        catch (ObjectDisposedException)
                        {
                            // Exit immediately
                            return;
                        }
                    }

                    // Are we exiting?
                    if (!pIsAlive)
                        return;

                    // If something is waiting to execute, do it now
                    if (method != null)
                    {
                        try
                        {
                            // Add to the busy thread count
                            Interlocked.Increment(ref pBusyThreads);

                            // Invoke the method
                            method.Method.Invoke(method.State);
                        }
#if !PocketPC || Framework20
                        catch (ThreadAbortException)
                        {
                            // Just exit!
                            return;
                        }
#endif
                        catch
                        {
                            if (!pIsAlive)
                                return;
                        }
                        finally
                        {
                            // And back off the busy thread count
                            Interlocked.Decrement(ref pBusyThreads);
							// And we're done with the method
							method = null;
                        }
                    }

                    // Are we exiting?
                    if (!pIsAlive)
                        return;

                    // Are we over the maximum thread limit?  If so, exit
                    lock (pThreads.SyncRoot)
                    {
                        if (pThreads.Count > pMaximumThreads)
                            return;
                    }

                    // Have we been idle for more than the time period?  If so, exit
                    TimeSpan TotalIdleTime = DateTime.Now.Subtract(LastExecutedMethod);
                    if (TotalIdleTime > pMaximumIdleTime)
                        return;

                    // Let other threads work
                    Thread.Sleep(0);
                }
            }
#if !PocketPC || Framework20
            catch (ThreadAbortException)
            {
                // Just exit!
                return;
            }
#endif
            catch
            {
                // Ignore errors since we're on another thread.
            }
            finally
            {
                // Remove the thread from the pool
                lock (pThreads.SyncRoot)
                {
                    if (pThreads.Contains(Thread.CurrentThread))
                        pThreads.Remove(Thread.CurrentThread);
                }
            }
		}
	}
}
#endif